package com.md.domain.service;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.md.application.assembler.ApiAssembler;
import com.md.application.assembler.AppInfoAssembler;
import com.md.application.assembler.ModuleAssembler;
import com.md.base.exception.ServerException;
import com.md.domain.model.bo.QueryApiBO;
import com.md.domain.model.bo.QueryAppInfoBO;
import com.md.domain.model.bo.QueryModuleBO;
import com.md.domain.model.po.ApiPO;
import com.md.domain.model.po.AppInfoPO;
import com.md.domain.model.po.ModuleAppPO;
import com.md.domain.model.po.ModulePO;
import com.md.domain.repository.ApiRepository;
import com.md.domain.repository.AppInfoRepository;
import com.md.domain.repository.ModuleAppRepository;
import com.md.domain.repository.ModuleRepository;
import com.md.infrastructure.value.ErrorEnum;
import com.md.model.page.PageResponse;
import com.md.response.ApiResponse;
import com.md.response.AppInfoResponse;
import com.md.response.ModuleResponse;
import com.md.util.ClassUtils;
import com.md.util.CollectionUtil;
import com.md.util.ObjectUtil;
import com.md.util.PageUtil;
import com.md.value.constants.DefaultServerContext;
import com.md.value.server.ServerContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

import static com.md.application.assembler.AppInfoAssembler.buildModuleLinkList;
import static com.md.infrastructure.value.ErrorEnum.MODULE_IS_EXIST;
import static com.md.infrastructure.value.ErrorEnum.MODULE_NOT_EXIST;

/**
 * @author 刘骄阳
 * 2022-09-24 1:33
 */
@Service
@RequiredArgsConstructor
public class AppDomainService {
  private final AppInfoRepository appInfoRepository;
  private final ModuleRepository moduleRepository;
  private final ModuleAppRepository moduleAppRepository;
  private final ApiRepository apiRepository;

  public PageResponse<AppInfoResponse> queryAppInfoByPage(final QueryAppInfoBO queryBO) {
    return AppInfoAssembler.buildAppInfoPO2Response(
      appInfoRepository.selectPage(AppInfoAssembler.buildAppInfoPO(queryBO),
        queryBO.getPageNum(), queryBO.getPageSize()));
  }

  public AppInfoResponse query(final QueryAppInfoBO queryAppInfoBO) {
    List<AppInfoResponse> list = queryAppInfo(queryAppInfoBO);
    if (ObjectUtil.isEmpty(list)) {
      throw ServerException.of(ErrorEnum.APP_INFO_NOT_EXIST);
    }
    return list.get(0);
  }

  public List<AppInfoResponse> queryAppInfo(final QueryAppInfoBO queryBO) {
    AppInfoAssembler.checkParamValue(queryBO.getId(), queryBO.getModuleId(), queryBO.getAppCode(),
      queryBO.getTenantId(), queryBO.getBrandId());
    return AppInfoAssembler.buildAppInfoPO2Response(
      appInfoRepository.selectList(AppInfoAssembler.buildAppInfoPO(queryBO)));
  }

  public List<AppInfoResponse> queryAppInfo(final List<Long> ids) {
    return AppInfoAssembler.buildAppInfoPO2Response(
      appInfoRepository.selectByIds(ids));
  }

  public AppInfoResponse queryAppInfo(final Long id) {
    return AppInfoAssembler.buildAppInfoPO2Response(
      appInfoRepository.selectById(id));
  }

  public ModuleResponse queryModuleDetail(final ModulePO module) {
    ModuleAssembler.checkParamValue(module.getId(), module.getModelName(),
      module.getTenantId(), module.getBrandId());
    return ModuleAssembler.buildModulePO2Response(moduleRepository.select(module));
  }

  public PageResponse<ModuleResponse> queryModuleByPage(final QueryModuleBO queryBO) {
    ModulePO modulePO = ModuleAssembler.buildModulePO(queryBO);
    modulePO.setUserId(ServerContext.getLong(DefaultServerContext.USER_ID).toString());
    IPage<ModulePO> modulePOIPage = moduleRepository.selectPage(modulePO,
      queryBO.getPageNum(), queryBO.getPageSize());
    return PageUtil.buildPageResponse(modulePOIPage, ModuleAssembler::buildModulePO2Response);
  }

  public List<ModuleResponse> queryModuleList(final QueryModuleBO queryBO) {
    List<ModulePO> modulePOList = moduleRepository.selectList(ModuleAssembler.buildModulePO(queryBO));
    return CollectionUtil.map(modulePOList, ModuleAssembler::buildModulePO2Response);
  }

  public List<ModuleResponse> queryModuleListByIdList(final List<Long> idList) {
    if (ObjectUtil.isEmpty(idList)) {
      return Lists.newArrayList();
    }
    List<ModulePO> modulePOList = moduleRepository.selectByIds(idList);
    return CollectionUtil.map(modulePOList, ModuleAssembler::buildModulePO2Response);
  }

  public List<ApiResponse> queryApiList(final QueryApiBO queryBO) {
    List<ApiPO> apiList = apiRepository.selectList(ApiAssembler.buildApiPO(queryBO));
    return ApiAssembler.buildApiPO2Response(apiList);
  }

  public ApiResponse queryApi(final QueryApiBO queryBO) {
    return ApiAssembler.buildApiPO2Response(apiRepository.select(ApiAssembler.buildApiPO(queryBO)));
  }

  @Transactional
  public void createAppInfo(final AppInfoPO appInfoPO, final Long moduleId) {
    ModulePO modulePO = moduleRepository.selectById(moduleId);
    if (ObjectUtil.isEmpty(modulePO)) {
      throw ServerException.build(MODULE_NOT_EXIST);
    }
    String userId = ServerContext.getLong(DefaultServerContext.USER_ID).toString();
    checkUserModule(moduleId, userId);
    ServerContext.put(DefaultServerContext.TENANT_ID, modulePO.getTenantId());
    ServerContext.put(DefaultServerContext.BRAND_ID, appInfoPO.getBrandId());
    appInfoPO.setUserId(userId);
    appInfoPO.buildExtra();
    appInfoRepository.insert(appInfoPO);
    Long appInfoId = appInfoPO.getId();
    ModuleAppPO moduleApp = new ModuleAppPO();
    moduleApp.setAppInfoId(appInfoId);
    moduleApp.setModuleId(moduleId);
    moduleAppRepository.insert(moduleApp);
  }

  @Transactional
  public void createModule(final ModulePO modulePO) {
    modulePO.setUserId(Optional.ofNullable(ServerContext.getLong(DefaultServerContext.USER_ID)).orElse(0L).toString());
    moduleRepository.insert(modulePO);
  }

  @Transactional
  public void createApi(final ApiPO apiPO) {
    String url = apiPO.getUrl();
    ApiPO exist = new ApiPO();
    exist.setUrl(url);
    exist.setMethod(apiPO.getMethod());
    if (apiRepository.hasExists(exist)) {
      return;
    }
    apiRepository.insert(apiPO);
  }

  @Transactional
  public void updateAppInfo(final AppInfoPO appInfoPO) {
    // 查询是否存在
    Long id = appInfoPO.getId();
    AppInfoPO dbAppInfoPO = appInfoRepository.selectById(id);
    if (ObjectUtil.isEmpty(dbAppInfoPO)) {
      throw ServerException.of(ErrorEnum.APP_INFO_NOT_EXIST);
    }
    AppInfoPO updateDBAppInfoPO = ClassUtils.copyProperty(appInfoPO, AppInfoPO::getId, AppInfoPO::getAppName,
      AppInfoPO::getAppEnName, AppInfoPO::getAppLogo, AppInfoPO::getAppExtra);
    updateDBAppInfoPO.buildExtra();
    appInfoRepository.update(updateDBAppInfoPO);
  }

  @Transactional
  public void update(final ModulePO modulePO) {
    // 查询是否存在
    Long id = modulePO.getId();
    ModulePO dbModulePO = moduleRepository.selectById(id);
    if (ObjectUtil.isEmpty(dbModulePO)) {
      throw ServerException.of(ErrorEnum.APP_INFO_NOT_EXIST);
    }
    ModulePO updateDBModulePO = ClassUtils.copyProperty(modulePO, ModulePO::getId, ModulePO::getModelName,
      ModulePO::getModelEnName, ModulePO::getModelLogo, ModulePO::getModelDescription);
    moduleRepository.update(updateDBModulePO);
  }

  public void linkModule(final Long moduleId, final List<Long> appInfoIds) {
    if (ObjectUtil.isEmpty(appInfoIds)) {
      return;
    }
    List<Long> existAppInfoIds = CollectionUtil.map(moduleAppRepository.queryModuleLinkByAppInfoIds(moduleId,
      appInfoIds), ModuleAppPO::getAppInfoId);
    appInfoIds.removeAll(existAppInfoIds);
    moduleAppRepository.insertBatch(buildModuleLinkList(moduleId, appInfoIds));
  }

  public void checkUserModule(final Long moduleId, final String userId) {
    // 校验模块是否是用户的模块
    if (ObjectUtil.isEmpty(moduleRepository.checkUserModule(moduleId, userId))) {
      throw ServerException.build(MODULE_NOT_EXIST);
    }
  }

  public void checkModuleExists(final String moduleName, final Long tenantId, final Long brandId,
                                final String userId) {
    if (moduleRepository.checkModuleExists(moduleName, tenantId, brandId, userId)) {
      throw ServerException.build(MODULE_IS_EXIST);
    }
  }

  public List<AppInfoResponse> queryListByModuleId(final Long moduleId) {
    List<ModuleAppPO> moduleAppPOS = moduleAppRepository.queryModuleLinkByModuleId(moduleId);
    if (ObjectUtil.isEmpty(moduleAppPOS)) {
      return Lists.newArrayList();
    }
    List<Long> appInfoIdList = CollectionUtil.map(moduleAppPOS, ModuleAppPO::getAppInfoId);
    return AppInfoAssembler.buildAppInfoPO2Response(appInfoRepository.selectByIds(appInfoIdList));
  }
}
